package com.webchat.sso.service;

import com.webchat.common.bean.APIResponseBean;
import com.webchat.common.bean.APIResponseBeanUtil;
import com.webchat.common.constants.WebConstant;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.RoleCodeEnum;
import com.webchat.common.enums.UserStatusEnum;
import com.webchat.common.exception.AuthException;
import com.webchat.common.exception.BusinessException;
import com.webchat.common.service.RedisService;
import com.webchat.common.service.SnowflakeIdGeneratorService;
import com.webchat.common.util.IDGenerateUtil;
import com.webchat.common.util.MD5Utils;
import com.webchat.common.util.StringUtil;
import com.webchat.common.util.TransactionSyncManagerUtil;
import com.webchat.domain.vo.request.UserLoginInfoRequestVO;
import com.webchat.domain.vo.request.UserRegistryInfoRequestVO;
import com.webchat.domain.vo.response.UserBaseResponseVO;
import com.webchat.rmi.user.UserServiceClient;
import com.webchat.sso.config.properties.SSOConfigProperties;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.regex.Pattern;

@Service
public class SSOCoreService {

    @Autowired
    private UserServiceClient userServiceClient;

    @Autowired
    private RedisService redisService;

    @Autowired
    private SSOConfigProperties ssoConfigProperties;

    @Autowired
    private SnowflakeIdGeneratorService snowflakeIdGeneratorService;

    /**
     * 登录授权码兑换次数限制
     */
    private static final int MAX_EXCHANGE_COUNT = 2;

    /**
     * 账号登录服务
     *
     * @param loginInfoRequest
     * @return
     */
    public String login(UserLoginInfoRequestVO loginInfoRequest, HttpServletResponse httpServletResponse) {
        // 1. 查询账号是否存在
        UserBaseResponseVO user = this.getUserBaseInfo(loginInfoRequest.getMobile());
        Assert.notNull(user, "账号不存在！");
        // 2. 校验账号密码
        String inputPassword = MD5Utils.md5(loginInfoRequest.getPassword().concat(WebConstant.MD5_SALT));
        if (!ObjectUtils.equals(inputPassword, user.getPassword())) {
            throw new AuthException("密码错误！");
        }
        // 生成token value
        String token = snowflakeIdGeneratorService.generateId();
        // 3. 缓存token对应用户信息
        doCacheUserToken(user.getUserId(), token);
        // 4. 种泛域名/大域名登录用户cookie
        createLoginCookie(httpServletResponse, token);
        // 4. 生成登录授权码
        return genLoginOauthCode(user.getUserId());
    }

    /**
     * 用户注册
     *
     * @param request
     * @return
     */
    // 这里其实不需要事务，因为想分享事务管理器的用法和场景，刻意添加了事务注解
    public boolean registry(UserRegistryInfoRequestVO request) {
        APIResponseBean<Boolean> responseBean = userServiceClient.registry(request);
        if (APIResponseBeanUtil.isOk(responseBean)) {
            return responseBean.getData();
        }
        return false;
    }

    /**
     * 判断用户是否存在
     * @param mobile
     * @return
     */
    private boolean hasUserInfo(String mobile) {
        UserBaseResponseVO userBaseResponseVO = getUserBaseInfo(mobile);
        return userBaseResponseVO != null;
    }

    private UserBaseResponseVO getUserBaseInfo(String mobile) {
        APIResponseBean<UserBaseResponseVO> responseMessage = userServiceClient.userBaseInfo(mobile);
        if (APIResponseBeanUtil.isOk(responseMessage)) {
            return responseMessage.getData();
        }
        throw new AuthException("用户不存在");
    }

    /**
     * 根据token解析登录用户id
     *
     * @param token
     * @return
     */
    public String getUserIdByToken(String token) {
        String userTokenCacheKey = userTokenCacheKey(token);
        return redisService.get(userTokenCacheKey);
    }

    /**
     * 缓存登录用户token（默认24小时有效）
     *
     * @param userId
     * @param token
     */
    private void doCacheUserToken(String userId, String token) {
        String userTokenCacheKey = userTokenCacheKey(token);
        redisService.set(userTokenCacheKey, userId, RedisKeyEnum.LOGIN_USER_TOKEN.getExpireTime());
    }

    /**
     * 登录token续期
     *
     * @param token
     */
    private void incrementUserTokenCacheExpireTime(String token) {
        String userTokenCacheKey = userTokenCacheKey(token);
        redisService.expire(userTokenCacheKey, RedisKeyEnum.LOGIN_USER_TOKEN.getExpireTime());
    }

    private String userTokenCacheKey(String token) {
        return RedisKeyEnum.LOGIN_USER_TOKEN.getKey(token);
    }

    /**
     * 种用户登录凭证
     *
     * @param response
     * @param token
     */
    private void createLoginCookie(HttpServletResponse response, String token) {
        Cookie cookie = new Cookie(ssoConfigProperties.getTokenName(), token);
        cookie.setDomain(ssoConfigProperties.getDomains());
        cookie.setPath("/");
        cookie.setMaxAge(24 * 60 * 60);
        response.addCookie(cookie);
    }

    /**
     * 生成登录授权码
     *
     * @return
     */
    private String genLoginOauthCode(String userId) {
        // 生成登录授权码
        String oauthCode = IDGenerateUtil.uuid();
        String cacheKey = RedisKeyEnum.OAUTH_CODE_CACHE.getKey(oauthCode);
        redisService.set(cacheKey, userId, RedisKeyEnum.OAUTH_CODE_CACHE.getExpireTime());
        return oauthCode;
    }

    /**
     * 根据授权码获取用户ID
     *
     * @param oauthCode
     * @return
     */
    public String getUserIdByOauthCode(String oauthCode) {
        Long exchangeCount = this.getOauthCodeExchangeCountCache(oauthCode);
        if (exchangeCount >= MAX_EXCHANGE_COUNT) {
            throw new AuthException("授权码使用次数过多");
        }
        String cacheKey = RedisKeyEnum.OAUTH_CODE_CACHE.getKey(oauthCode);
        String userId = redisService.get(cacheKey);
        if (StringUtils.isNotBlank(userId)) {
            this.addExchangeCountCache(oauthCode);
            return userId;
        }
        throw new AuthException("授权码无效");
    }

    private void addExchangeCountCache(String oauthCode) {
        String cacheKey = RedisKeyEnum.OAUTH_CODE_GET_COUNT_CACHE.getKey(oauthCode);
        redisService.increx(cacheKey, RedisKeyEnum.OAUTH_CODE_GET_COUNT_CACHE.getExpireTime());
    }

    private Long getOauthCodeExchangeCountCache(String oauthCode) {
        String cacheKey = RedisKeyEnum.OAUTH_CODE_GET_COUNT_CACHE.getKey(oauthCode);
        String cache = redisService.get(cacheKey);
        return StringUtils.isBlank(cache) ? 0L : Long.valueOf(cache);
    }
}
